<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>平面扩散波纹</title>
    <style>
      body {
        margin: 0;
        overflow: hidden;
        /* 隐藏body窗口区域滚动条 */
      }
    </style>
  </head>
  <body>
    <script type="importmap">
      {
        "imports": {
          "three": "./three.js-dev/build/three.module.js",
          "three/addons/": "./three.js-dev/examples/jsm/"
        }
      }
    </script>
    <script type="module">
      import * as THREE from 'three'
      import { MapControls } from 'three/addons/controls/OrbitControls.js'
      window.THREE = THREE
      // 创建场景对象
      var scene = new THREE.Scene()

      // 平行光
      var directionalLight = new THREE.DirectionalLight(0xffffff, 1)
      directionalLight.position.set(20, 20, 20)

      //环境光
      var ambient = new THREE.AmbientLight(0x444444)
      scene.add(ambient)

      // 相机设置
      var width = window.innerWidth
      var height = window.innerHeight
      //创建相机对象
      var camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000)
      camera.position.set(-6, 5, 15) //设置相机位置
      camera.lookAt(new THREE.Vector3(0, 0, 0))

      // 创建渲染器对象
      var renderer = new THREE.WebGLRenderer()
      renderer.setSize(width, height) //设置渲染区域尺寸
      // renderer.setClearColor(0xb9d3ff, 1) //设置背景颜色
      document.body.appendChild(renderer.domElement) //body元素中插入canvas对象
      let controls = new MapControls(camera, renderer.domElement) //创建控件对象
      // 辅助坐标系  参数300表示坐标系大小，可以根据场景大小去设置
      let axesHelper = new THREE.AxesHelper(300)
      scene.add(axesHelper)
      // 顶点着色器代码
      const vertexShader = `  
  varying vec2 vUv;
  void main() { 
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  }`
      // 片元着色器代码
      const fragmentShader = `
  varying vec2 vUv;
  // 下面是传进来的变量
  uniform vec3 uColor;
  uniform float uOpacity;
  uniform float uRange;
  uniform float uSpeed; 
  uniform float uSge;
  uniform float time;
  float PI = 3.14159265;
  float drawCircle(float index, float range) {
      float opacity = 1.0;
      if (index >= 1.0 - range) {
          opacity = 1.0 - (index - (1.0 - range)) / range;
      } else if(index <= range) {
          opacity = index / range;
      } 
      return opacity;
  }
  void main() { 
      float iTime = -time * uSpeed;
      float opacity = 0.0;
      float len = distance(vec2(0.5, 0.5), vec2(vUv.x, vUv.y)); 

      float size = 1.0 / uSge;
      float rSize = uRange / 2.0;
      vec2 range = vec2(0.7 - rSize, 0.7 + rSize);
      float index = mod(iTime + len, size); 
      // 中心圆 
      vec2 cRadius = vec2(0.06, 0.12); 
      
      if (index < size && len <= 0.5) {   
          float i = sin(index / size * PI); 

          // 处理边缘锯齿
          if (i >= range.x && i <= range.y){
              // 归一
              float t = (i - range.x) / (range.y - range.x);
              // 边缘锯齿范围
              float r = 0.3;
              opacity = drawCircle(t, r);
              
          }
          // 渐变
          opacity *=  1.0 - len / 0.5;
      }; 
      
      gl_FragColor = vec4(uColor, uOpacity * opacity);
  }`
      // 相关的配置参数
      const config = {
        width: 10,
        widthSegments: 8,
        color: 0xff91c2,
        opacity: 1,
        range: 0.5,
        speed: 0.1,
        seg: 6,
      }
      const geometry = new THREE.PlaneGeometry(
        config.width,
        config.width,
        config.widthSegments,
        config.widthSegments
      )
      // 使用自定义着色器构建材质
      const material = new THREE.ShaderMaterial({
        uniforms: {
          uColor: { value: new THREE.Color(config.color) },
          uOpacity: { value: config.opacity },
          uRange: { value: config.range }, // 圆环的大小
          uSpeed: { value: config.speed }, // 扩散的速度
          uSge: { value: config.seg }, // 圆环个数
          uRadius: { value: config.width / 2 },
          time: { value: 0 },
        },
        // opacity:0.5,
        transparent: true,
        vertexShader: vertexShader, // 顶点着色器
        fragmentShader: fragmentShader, // 片元着色器
        side: THREE.DoubleSide,
      })
      // 将材质绘制到一个平面上
      const model = new THREE.Mesh(geometry, material)
      scene.add(model)
      function render() {
        requestAnimationFrame(render) //请求再次执行渲染函数render
        controls.update()
        material.uniforms['time'].value += 0.02
        renderer.render(scene, camera)
      }
      render()
    </script>
  </body>
</html>
